<?php
/**
 * Created by 784855684@qq.com.
 * Link: https://github.com/lichtung/inframe
 * User: linzh
 * Date: 6/25/17
 * Time: 11:18 AM
 */


namespace {

    const IN_VERSION = '0.7';
    # 外部可定义应用名称,默认为空
    defined('IN_APP_NAME') or define('IN_APP_NAME', '');
    # 定义debug模式是否开启,默认开启
    defined('IN_DEBUG_MODE_ON') or define('IN_DEBUG_MODE_ON', true);
    # 定义是否是生产环境,默认为false
    defined('IN_IS_PRODUCT') or define('IN_IS_PRODUCT', false);
    # 固定常量
    define('IN_NOW_MICRO', (float)(isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(true)));
    define('IN_NOW', (int)(isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time()));
    define('IN_MEM', memory_get_usage());

    const IN_ONE_HOUR = 3600;
    const IN_ONE_DAY = 86400;

    const IN_ADAPTER_CLASS = 'IN_ADAPTER_CLASS';
    const IN_ADAPTER_CONFIG = 'IN_ADAPTER_CONFIG';

    # 布尔常量
    define('IN_IS_WIN', false !== strpos(strtoupper(PHP_OS), 'WIN'));
    const IN_IS_CLI = PHP_SAPI === 'cli';

    # 目录常量
    define('IN_PATH_BASE', dirname(__DIR__) . '/'); # 根目录
    const IN_PATH_FRAMEWORK = __DIR__ . '/';    # 框架目录
    const IN_PATH_APP = IN_PATH_BASE . IN_APP_NAME . '/'; # 应用目录
    const IN_PATH_DATA = IN_PATH_APP . 'data/'; # 数据目录
    const IN_PATH_VENDOR = IN_PATH_APP . 'vendor/'; # 第三方目录
    const IN_PATH_MODULE = IN_PATH_APP . 'module/';# 应用模块目录
    const IN_PATH_CONFIG = IN_PATH_APP . 'config/'; #中配置目录
    const IN_PATH_RUNTIME = IN_PATH_APP . 'runtime/';# 运行时目录
    const IN_PATH_PUBLIC = IN_PATH_APP . 'public/'; # 公开目录,存放入口文件和其他资源文件
    const IN_PATH_UPLOAD = IN_PATH_APP . 'upload/'; # 上传文件

    # 语言
    const IN_LANG_ZH_CN = 'zh-CN';
    const IN_LANG_ZH_TW = 'zh-TW';
    const IN_LANG_EN_US = 'en-US';
    const IN_LANG_ZH = 'zh';
    const IN_LANG_EN = 'en';

    # 编码
    const IN_CHARSET_UTF8 = 'UTF-8';
    const IN_CHARSET_GBK = 'GBK';
    const IN_CHARSET_ASCII = 'ASCII';
    const IN_CHARSET_GB2312 = 'GB2312';
    const IN_CHARSET_LATIN1 = 'ISO-8859-1';# 欧洲部分国家使用,Latin1是ISO-8859-1的别名(西欧语言)

    # 数据类型
    const IN_TYPE_BOOL = 'boolean';
    const IN_TYPE_INT = 'integer';
    const IN_TYPE_FLOAT = 'double';//double ,  float
    const IN_TYPE_STR = 'string';
    const IN_TYPE_ARRAY = 'array';
    const IN_TYPE_OBJ = 'object';
    const IN_TYPE_RESOURCE = 'resource';
    const IN_TYPE_NULL = 'NULL';
    const IN_TYPE_UNKNOWN = 'unknown type';


    # storage 方案
    const IN_STORAGE_REDIS = 'redis';
    const IN_STORAGE_FILE = 'file';
    const IN_STORAGE_MEMCACHED = 'memcached';
    const IN_STORAGE_SQLITE = 'sqlite';

    # 其他常量
    const IN_DAO_DSN = 'DSN';
    const IN_DAO_HOST = 'HOST';
    const IN_DAO_PORT = 'PORT';
    const IN_DAO_DBNAME = 'DB';
    const IN_DAO_USERNAME = 'USER';
    const IN_DAO_PASSWORD = 'PWD';
    const IN_DAO_CHARSET = 'CHARSET';
    const IN_DAO_OPTIONS = 'OPT';

    const IN_FROM_REQUEST = 0;
    const IN_FROM_GET = 1;
    const IN_FROM_POST = 2;
    const IN_FROM_INPUT = 3;

    if (!IN_IS_CLI) {
        # web模式
        error_reporting(IN_DEBUG_MODE_ON ? E_ALL : 0);
        define('IN_IS_AJAX', isset($_SERVER['HTTP_X_REQUESTED_WITH']) and $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest');
        define('IN_REQUEST_METHOD', strtoupper($_SERVER['REQUEST_METHOD']));//'GET', 'HEAD'，'POST'，'PUT' ...
        define('IN_IS_POST', IN_REQUEST_METHOD === 'POST');
        $dir = dirname($_SERVER['SCRIPT_NAME']);
        define('IN_IS_HTTPS',
            (
                (
                    isset($_SERVER['HTTPS']) and
                    (
                        ($_SERVER['HTTPS'] == 'on') or ($_SERVER['HTTPS'] == '1')
                    )
                ) or
                $_SERVER['SERVER_PORT'] == 443
            )
            or
            (
                !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ||
                !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on'
            )
        );
        # httpd服务标识符 由服务器IP和端口组成,用于集群环境区别服务器
        define('IN_SERVER_ID', (isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '') . ':' . (isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : ''));
        # host路径
        define('IN_HOST_URL', (IN_IS_HTTPS ? 'https://' : 'http://') . $_SERVER['HTTP_HOST']);
        # 公用路径,nginx作负载均衡时会变成特殊值,需要手动设定
        defined('IN_PUBLIC_URL') or define('IN_PUBLIC_URL', strlen($dir) === 1 ? IN_HOST_URL : IN_HOST_URL . (IN_IS_WIN ? str_replace('\\', '/', $dir) : $dir));//默认index
    }

    /**
     * 将参数二的配置合并到参数一种，如果存在参数一数组不存在的配置项，跳过其设置
     * (不会覆盖)
     * @param array $dest 被覆盖者
     * @param array $sourse 覆盖者
     * @param bool $covermode 为true的情况下无论目标健是否存在否会覆盖，否则会先检测目标是否存在
     * @return array
     */
    function ws_merge(array $dest, array $sourse, $covermode = true)
    {
        if ($covermode) {
            foreach ($sourse as $key => $val) {
                //fugei mose
                if (isset($dest[$key]) and is_array($val)) {
                    $dest[$key] = ws_merge($dest[$key], $val, true);/* se shuju,zo erbi tyi */
                } else {
                    if (is_int($key)) {
                        if (!in_array($val, $dest)) $dest[] = $val;
                    } else {
                        $dest[$key] = $val;
                    }
                }
            }
        } else {
            foreach ($sourse as $key => $val) {
                //kno saiqu wumao sai
                if (isset($dest[$key])) {
                    if (is_array($val)) {
                        $dest[$key] = ws_merge($dest[$key], $val, false);
                    } else {
                        if (is_int($key)) {
                            if (!in_array($val, $dest)) $dest[] = $val;
                        } else {
                            $dest[$key] = $val;
                        }
                    }
                }
            }
        }
        return $dest;
    }

    if (IN_DEBUG_MODE_ON) {
        require __DIR__ . '/include/dev.inc';
    } else {

        function dump(...$params)
        {
        }

        function dumpout(...$params)
        {
        }

        class ws_class_dvlp
        {
            public static function status($tag)
            {
            }

            public static function openTrace()
            {
            }

            public static function closeTrace()
            {
            }

            public static function import($tag, array $status)
            {
            }

            public static function trace($message = null)
            {
            }
        }
    }

}

namespace inframe {

    use inframe;
    use inframe\throws\ParametersInvalidException;

    /**
     * Class Developer 开发类
     * @package inframe
     */
    class Developer extends \ws_class_dvlp
    {
    }


    /**
     * Class Exception 自定义异常
     * @package lite
     */
    class Exception extends \Exception
    {
        /**
         * Exception constructor.
         * @param array ...$params
         */
        public function __construct(...$params)
        {
            if (count($params) === 1) {
                $message = reset($params);
            }
            $message = var_export($params, true);
            parent::__construct($message);
        }

        /**
         * 跑出自身
         * @return void
         * @throws Exception
         */
        public static function throwing()
        {
            $instance = (new \ReflectionClass(static::class))->newInstanceArgs(func_get_args());
            throw new $instance;
        }

        /**
         * 异常转字符串
         * @return string
         */
        public function __toString()
        {
            return $this->getMessage();
        }
    }

    /**
     * Class Component 组件
     * @package inframe
     */
    abstract class Component
    {
        /**
         * 组件配置
         * @var array
         */
        protected $_config = [];

        /**
         * 上一次调用发生的错误
         * @var string
         */
        protected $_error = '';
        /**
         * 实例名称（作为同类不同实例之间的区别）
         * @var int|string
         */
        protected $_name = '';
        /**
         * 适配器对象
         * @var null|object|\PDO
         */
        protected $_adapter = null;
        /**
         * 适配器名称
         * @var null|string
         */
        protected $_adapterName = null;
        /**
         * 适配器构造参数
         * @var null|array
         */
        protected $_adapterParams = null;

        /**
         * Component constructor.
         * @param array $params 类的完整配置
         * @param int|string $name 驱动名称，默认为int(0)
         * @throws ParametersInvalidException
         */
        public function __construct(array $params = [], $name = 0)
        {
            # 加载类配置
            $_config = inframe::loadComponentConfig(static::class);

            $this->_name = $name;
            $this->_config = ws_merge($this->_config, $params ? ws_merge($_config, $params) : $_config);

            if (!empty($this->_config[IN_ADAPTER_CLASS]) /* 说明该类使用适配器(驱动器)驱动 */ and
                $adaptername = $this->_adapterName =
                    isset($this->_config[IN_ADAPTER_CLASS][$name]) ?
                        $this->_config[IN_ADAPTER_CLASS][$name] : reset($this->_config[IN_ADAPTER_CLASS])
            ) {
                if (!$adaptername) throw new ParametersInvalidException($name);
                # 配置中不存在'IN_ADAPTER_CLASS'，适配器为null
                $this->_adapterParams = isset($this->_config[IN_ADAPTER_CONFIG][$name]) ?
                    $this->_config[IN_ADAPTER_CONFIG][$name] : reset($this->_config[IN_ADAPTER_CONFIG]);

//                if (stripos(static::class, 'dao') !== false) {
//                    dumpout($_config, $this->_config, $name, $adaptername, $this->_adapterParams);
//                }

                $this->_adapter = new $adaptername($this->_adapterParams === false ? [] : $this->_adapterParams);
            }
        }

        /**
         * 实例,对于构造参数的实例为同一个实例
         * @param int $index
         * @param array $params 类的构造参数数组
         * @return object|\inframe\core\Dao 类实例
         */
        public static function getInstance($index = 0, array $params = [])
        {
            static $_instances = [];
            if (!isset($_instances[$key = md5(($clsnm = static::class) . '###' . $index)])) {
                $_instances[$key] = new $clsnm($params, $index);
            }
            return $_instances[$key];
        }

        /**
         * 获取驱动器信息
         * @return array
         */
        public function getAdapter()
        {
            return [$this->_adapter, $this->_adapterName, $this->_adapterParams];
        }

        /**
         * 魔术调用转适配器
         * @param string $name 方法名称
         * @param array $arguments 参数名称
         * @return mixed|null 如果目标不存在适配器，则返回null
         */
        public function __call($name, array $arguments)
        {
            return $this->_adapter ? call_user_func_array([$this->_adapter, $name], $arguments) : null;
        }

        /**
         * 静态魔术调用转默认实例
         * @param string $name 方法名称
         * @param array $arguments 参数名称
         * @return mixed
         */
        public static function __callStatic($name, array $arguments)
        {
            return call_user_func_array([self::getInstance(), $name], $arguments);
        }

        /**
         * 设置和获取错误
         * @param string $error
         * @return string 如果是设置错误，则返回之前的错误
         */
        public function getError($error = '')
        {
            if ($error) {
                $prev_error = $this->_error;
                $this->_error = $error;
                return $prev_error;
            } else {
                return $this->_error;
            }
        }

    }

    /**
     * Interface RouteInterface 路由接口
     * @package inframe
     */
    interface RouteInterface
    {
        /**
         * 获取pathinfo信息，各个实现需要自己解决获取问题
         * @return string
         */
        public function getPathInfo();

        /**
         * 解析URL成 "模块 + 控制器 + 操作 + 参数"序列
         * @param string $url 待解析URL
         * @return array 解析结果依次按照 "modules controller action parameters" 排列
         */
        public function parse($url);

        /**
         * 创建可被该路由类解析的URL
         * @param string $modules 模块序列，如'admin'或者'admin/member'(带子模块)
         * @param string $controller 控制器名称
         * @param string $action 操作名称
         * @param array $params 参数数组
         * @return string 返回可以访问的URL
         */
        public function create($modules, $controller, $action, array $params = []);
    }

}

namespace {

    use inframe\core\Cache;
    use inframe\core\Response;
    use inframe\core\Trace;
    use inframe\Developer;
    use inframe\RouteInterface;
    use inframe\throws\ActionNotFoundException;
    use inframe\throws\ControllerNotFoundException;
    use inframe\throws\ModuleNotFoundException;

    final class inframe
    {
        /**
         * @var array 运行时配置
         */
        private static $_config = [
            # 时区
            'timezone_zone' => 'Asia/Shanghai',

            'use_config_bundle' => true,
            # trace显示
            'show_trace' => true,

            # session处理
            'session_customed' => false,//是否开启session自定义
            'session_handler' => 'redis',
            'session_save_path' => 'tcp://127.0.0.1:6379',
            'session_expire' => IN_ONE_HOUR,

            # 默认模块,控制器,方法
            'default_modules' => '',
            'default_controller' => 'index',
            'default_action' => 'index',

            # 行为(error_handler,exception_handler设置为false将不设置)
            'autoloader' => null,
            'error_handler' => null,
            'exception_handler' => null,

            # 操作的参数来源
            'params_from' => IN_FROM_REQUEST,
            # 默认路由处理类
            'route_handler' => '\\inframe\\core\\Router',

            'cdn_on' => false,
            'cdn_path' => '',

            # 静态缓存
            'static_cache_on' => false,
            'static_cache_driver' => 'redis',

            # 管理员地址,可以是多个
            'admin' => '784855684@qq.com',

        ];

        private static $_midware = [];

        private static $_sessionHandler = null;

        /**
         * @return SessionHandlerInterface
         */
        public static function getSessionHandler()
        {
            return self::$_sessionHandler;
        }

        /**
         * 获取配置
         * @param string $name
         * @param mixed $replacement
         * @return mixed
         */
        public static function getConfig($name, $replacement = null)
        {
            return isset(self::$_config[$name]) ? self::$_config[$name] : $replacement;
        }

        /**
         * 注册中间件
         * @param string $midware
         * @_param bool $prepend
         * @return void
         */
        public static function register($midware)
        {
            self::$_midware[] = $midware;
        }

        /**
         * 初始化
         * @param array $config
         * @return void
         */
        public static function initialize(array $config = [])
        {
            Developer::status('init_begin');
            # 加载配置
            $config and self::$_config = ws_merge(self::$_config, $config);
            if (isset(self::$_config['use_config_bundle']) ? self::$_config['use_config_bundle'] : false) {
                self::$_config = ws_merge(self::$_config, is_file($cfile = IN_PATH_APP . 'config/application.php') ? include $cfile : []);
            }
            # 时区
            date_default_timezone_set(self::$_config['timezone_zone']) or die('Date default timezone set failed!');
            # 设置自动加载
            self::setAutoload(self::$_config['autoloader']);
            # 会话
            if (self::$_config['session_customed']) {
                $sessionHandler = self::$_config['session_handler'];
                if (strpos($sessionHandler, '\\') !== false) {
                    # 保存位置为空,则说明使用了class
                    false === session_set_save_handler(self::$_sessionHandler = new $sessionHandler(self::$_config['session_expire'])) and die('register session handler failed');
                } else {
                    false === ini_set('session.save_handler', self::$_config['session_handler']) and die('set session.save_handler failed');
                    false === ini_set('session.save_path', self::$_config['session_save_path']) and die('set session.save_path failed');
                    false === ini_set('session.gc_maxlifetime', (string)self::$_config['session_expire']) and die('set session.gc_maxlifetime failed');
                    false === ini_set('session.cache_expire', (string)self::$_config['session_expire']) and die('set session.cache_expire failed');
                }
            }
            define('ADMIN_EMAIL', self::$_config['admin']);
            if (!IN_IS_CLI) {
                if (self::$_config['cdn_on'] and self::$_config['cdn_path']) {
                    define('IN_CDN', self::$_config['cdn_path']);
                } else {
                    # 源站
                    define('IN_CDN', IN_PUBLIC_URL);
                }
                # 脚本结束回掉
                register_shutdown_function(function () {/* 脚本结束时缓存区域的内容将会自动输出，所以不能把输出控制语句放到这里 */
                    Developer::status('engine_done');
                    if (!IN_IS_AJAX and self::$_config['show_trace']) Developer::trace(null); # ajax或者明确关闭trace的情况下不输出
                });
                # 处理未捕获错误
                if (false !== self::$_config['error_handler']) {
                    set_error_handler(isset(self::$_config['error_handler']) ? self::$_config['error_handler'] : [self::class, 'handleError']);
                }
                # 处理未捕获异常
                if (false !== self::$_config['exception_handler']) {
                    set_exception_handler(isset(self::$_config['exception_handler']) ? self::$_config['exception_handler'] : [self::class, 'handleException']);
                }
            }
        }

        /**
         * 开启应用
         * @param RouteInterface|null $route 路由解析类实例
         * @param callable|null $dispatch 调度方法
         * @return void
         */
        public static function start(RouteInterface $route = null, callable $dispatch = null)
        {
            if (IN_IS_CLI) return;
            Developer::status('engine_start');
            # 解析路由
            if ($route === null) {
                $routerClassName = self::$_config['route_handler'];
                $route = new $routerClassName();
            }
            $url = $route->getPathInfo();
            Developer::status('parse_route_begin');
            $parse_result = $route->parse($url);

            # 定义调度常量( 由于方法不区分大小写 => 操作的名称统一转小写 )
            define('IN_REQUEST_MODULES', empty($parse_result['m']) ? self::$_config['default_modules'] : $parse_result['m']);
            define('IN_REQUEST_CONTROLLER', empty($parse_result['c']) ? self::$_config['default_controller'] : $parse_result['c']);
            define('IN_REQUEST_ACTION', strtolower(empty($parse_result['a']) ? self::$_config['default_action'] : $parse_result['a']));
            define('IN_REQUEST_PATH', IN_REQUEST_MODULES . '/' . IN_REQUEST_CONTROLLER . '/' . IN_REQUEST_ACTION);
            Developer::status('check_cache_begin');

            if (self::$_config['static_cache_on']) {
                #检查缓存
                $cacher = Cache::getInstance(self::$_config['static_cache_driver']);
                $content = $cacher->get(IN_REQUEST_PATH, null);
                if (null !== $content) {
                    header('lite-cache: reload ' . strlen($content));
                    exit($content);
                }
            } else {
                $cacher = $key = null;
            }

            Developer::status('dispatch_begin');

            # 执行调度
            $method = call_user_func_array(isset($dispatch) ? $dispatch : [self::class, 'dispatch'], [IN_REQUEST_MODULES, IN_REQUEST_CONTROLLER, IN_REQUEST_ACTION]);
            $arguments = [];
            switch (self::$_config['params_from']) {
                case IN_FROM_REQUEST:
                    $arguments = $_REQUEST;
                    break;
                case IN_FROM_GET:
                    $arguments = $_GET;
                    break;
                case IN_FROM_POST:
                    $arguments = $_POST;
                    break;
                case IN_FROM_INPUT:
                    parse_str(file_get_contents('php://input'), $arguments);
                    break;
                default:
                    $arguments = array_merge($_GET, $_POST);//POST覆盖GET
                    break;
            }

            if (self::$_config['static_cache_on']) {
                ob_start();
                $actionback = self::executeMethod($method, $arguments);
                $content = ob_get_clean();

                if (is_int($actionback)) {
                    header('lite-cache: build ' . strlen($content));
                    $cacher->set(IN_REQUEST_PATH, $content, $actionback);
                }
                echo $content;
            } else {
                self::executeMethod($method, $arguments);
            }
        }


        /**
         * 内置的调度器
         * @param string $modules 模块序列
         * @param string $ctrler 控制器
         * @param string $action 操作
         * @return ReflectionMethod 返回方法的反射
         * @throws ActionNotFoundException 方法不存在时抛出
         * @throws ControllerNotFoundException 控制器不存在时抛出
         * @throws ModuleNotFoundException 模块不存在时抛出
         */
        public static function dispatch($modules, $ctrler, $action)
        {
            $modulepath = IN_PATH_APP . "module/{$modules}";//linux 不识别
            strpos($modules, '/') and $modules = str_replace('/', '\\', $modules);
            //模块检测
            if (!is_dir($modulepath)) {
                throw new ModuleNotFoundException($modulepath);
            }
            //控制器检测
            $className = IN_APP_NAME . ($modules ? "\\module\\{$modules}" : '\\module') . "\\controller\\{$ctrler}";

            if (!class_exists($className, true)) {
                throw new ControllerNotFoundException($className);
            }

            try {
                $instance = new ReflectionMethod($className, $action);
            } catch (ReflectionException $e) {
                try {
                    $instance = new ReflectionMethod($className, '_empty');
                    $_REQUEST['method'] = $_REQUEST['method'] = $_REQUEST['method'] = $action;
                } catch (ReflectionException $e) {
                    throw new ActionNotFoundException($action);
                }
            }
            //方法检测
            return $instance;
        }


        /**
         * @var array 类名,路径映射
         */
        private static $_map = [];

        /**
         * 钩子集合
         * key可能是某种有意义的方法，也可能是一个标识符
         * value可能是闭包函数或者类名称
         * 如果是value类名称，则key可能是其调用的方法的名称（此时会检查这个类中是否存在这个方法），也可能是一个标识符
         * @var array
         */
        private static $_hook = [];
        /**
         * 类的加载规则
         *
         * //根命名空间
         * 'root_namespace' => [
         *      'path'   => '',//加载路径
         *      'ext'    => '',//类的后缀名
         * ],
         * 或者
         * 'root_namespace' => '[path]',//这种情况下后缀名默认为 .php
         *
         * 例子:
         * //laravel
         * 'Illuminate' => '/lite/vendor/laravel/',
         * //@see https://github.com/symfony/symfony
         * 'Symfony' => '/lite/vendor/',
         *
         * 如果类以‘ABS:’开头，则后面的路径为绝对地址,否则为相对项目根目录(naz存放的目录)的地址
         *
         * 如果自动加载的类无命名空间，需要将类放到根目录下的 classes 文件夹下
         * @var array
         */
        protected static $rules = [
            'Workerman' => 'inframe/plugin/',
            'Symfony' => 'inframe/plugin/',
        ];

        /**
         * @param callable|null $autoloader
         * @param bool $prepend
         * @return bool
         */
        public static function setAutoload(callable $autoloader = null, $prepend = true)
        {
            return spl_autoload_register(isset($autoloader) ? $autoloader : [static::class, 'load'], false, $prepend);
        }

        /**
         * 设置自动加载规则
         * @param string $begin what the class full name begin before first '\\'
         * @param string|array $rule
         * @return void
         */
        public static function setAutoloadRule($begin, $rule)
        {
            self::$rules[$begin] = $rule;
        }

        /**
         * 导入自动加载映射
         * @param array $map
         * @return void
         */
        public static function setAuthloadMap(array $map)
        {
            self::$_map = array_merge(self::$_map, $map);
        }

        /**
         * 设置钩子规则
         * @param array $hook
         * @return void
         */
        public static function setHookMap(array $hook)
        {
            self::$_hook = array_merge(self::$_hook, $hook);
        }

        /**
         * 设置钩子
         * @param string $tag
         * @param callable $behavior
         * @return void
         */
        public static function setHook($tag, callable $behavior)
        {
            if (!isset(self::$_hook[$tag])) self::$_hook[$tag] = [];
            self::$_hook[$tag][] = $behavior;
        }

        /**
         * 调用钩子
         * @param string $tag
         * @param $params
         * @return void
         */
        public static function hook($tag, $params)
        {
            static $_behaviours = [];
            if (!empty(self::$_hook[$tag])) foreach (self::$_hook[$tag] as $name) {
                if (is_callable($name)) {
                    //如果是闭包，则直接执行闭包函数
                    $res = $name($params);
                } elseif (is_string($name)) {
                    # 如果是类名称,则认为是一个行为类,调用其实例的run方法
                    isset($_behaviours[$name]) or $_behaviours[$name] = new $name();
                    if (!is_callable([$_behaviours[$name], $tag])) $tag = 'run';//tag默认是方法名称
                    $res = call_user_func_array([$_behaviours[$name], $tag], [$params, $tag]);
                } else {
                    $res = true;
                }
                if (false === $res) break; // 如果返回false 则中断行为执行
            }
        }

        /**
         * 根据类加载
         * @param string $clsnm
         * @return bool 返回类是否加载成功
         */
        public static function load($clsnm)
        {
            if (isset(self::$_map[$clsnm])) {
                require self::$_map[$clsnm];
                return true;
            } else {
                $pos = strpos($clsnm, '\\');

                if ($pos === false) {
                    # 不存在命名空间
                    $relapath = $clsnm;
                    $namespace = false;
                } else {
                    $relapath = str_replace('\\', '/', $clsnm);
                    $namespace = substr($clsnm, 0, $pos);# 命名空间的第一级
                }

                if (false !== $namespace) {

                    if (isset(self::$rules[$namespace])) {
                        //has namespace and has rule specialized
                        $rule = self::$rules[$namespace];
                        if (is_string($rule)) {
                            $path = $rule;
                            $ext = 'php';
                        } else {/* array */
                            $path = isset($rule['path']) ? $rule['path'] : '';
                            $ext = isset($rule['ext']) ? $rule['ext'] : 'php';//兼容
                        }
                        if (strpos($path, '@') === 0) {
                            # @开头的都是绝对路径
                            $path = ltrim($path, '@');
                        } else {
                            $path = IN_PATH_BASE . '/' . $path;
                        }
                        $path = "{$path}/{$relapath}.{$ext}";
                    } else {
                        $path = IN_PATH_BASE . "{$relapath}.php";
                    }
                } else {
                    # 直接在根路径下的寻找
                    $path = IN_PATH_BASE . "{$relapath}.php";
                }

                if (is_file($path)) {
                    require(self::$_map[$clsnm] = $path);
                    return true;
                } else {
                    return false;
                }
            }
        }


        /**
         * 处理错误
         * @param $code
         * @param $message
         * @param $file
         * @param $line
         * @return void
         */
        public static function handleError($code, $message, $file, $line)
        {
            Trace::handleTrowable($message, "Error {$code}", $file, $line, debug_backtrace());
        }

        /**
         * 处理异常
         * @param Throwable $e
         * @return void
         */
        public static function handleException($e)
        {
            Trace::handleTrowable($e->getMessage(), get_class($e), $e->getFile(), (int)$e->getLine(), $e->getTrace());
        }

        /**
         * 加载显示模板
         * @param string $tpl 模板的路径(参数4设置为true)或者模板文件的名称(template目录下)
         * @param array $vars 释放到模板中的变量名称
         * @param bool $clean 为true时将清空待输出的ob缓存
         * @param bool $isfile 判断是否是模板文件
         * @return void
         */
        public static function template($tpl, array $vars = [], $clean = false, $isfile = false)
        {
            $clean and ob_get_level() > 0 and ob_end_clean();
            $isfile or $tpl = IN_PATH_FRAMEWORK . "template/{$tpl}.php";
            if (!is_file($tpl)) {
                $vars['error'] = "'{$tpl}' not found";
                $tpl = IN_PATH_FRAMEWORK . 'template/error.php';
            }
            $vars and extract($vars, EXTR_OVERWRITE);
            include $tpl;
        }

        /**
         * 获取组件配置
         * 组件的配置名为类名中的'\'替换为'.',并且全部转小写
         * @param string $clsnm
         * @return array
         */
        public static function loadComponentConfig($clsnm)
        {
            $clsnm = strtolower(str_replace('\\', '.', trim($clsnm, '\\')));
            return isset(self::$_config[$clsnm]) ? self::$_config[$clsnm] : [];
        }

        /**
         * 执行方法
         * 注:只能通过class属性得到名称，所以猜测ReflectionMethod反射的原理是用过加载进内存中的类的二进制代码来获取参数信息的，而构造中参数以可以是实力对象，也是想依赖这个对象得到类名称
         * @param ReflectionMethod $method 方法反射对象
         * @param array $arguments 方法参数数组
         * @return mixed 返回方法执行结果
         * @throws Exception
         */
        public static function executeMethod(ReflectionMethod $method, array $arguments)
        {
            if (!$method->isPublic() or $method->isStatic()) {
                throw new ActionNotFoundException($method->name);
            }
            $clsnm = $method->class;
            $instance = new $clsnm();

            //方法的参数检测
            if ($method->getNumberOfParameters()) {//有参数
                $args = [];
                /** @var \ReflectionParameter[] $methodParams */
                $methodParams = $method->getParameters();
                if ($methodParams) {
                    //参数组织
                    foreach ($methodParams as $param) {
                        $paramName = $param->getName();
                        if (isset($arguments[$paramName])) {
                            $args[] = $arguments[$paramName];
                        } elseif ($param->isDefaultValueAvailable()) {
                            $args[] = $param->getDefaultValue();
                        } else {
                            Response::cleanOutput();
                            die("'$paramName' missing");
                        }
                    }
                }
                //执行方法
                $result = $method->invokeArgs($instance, $args);
            } else {//无参数的方法调用
                $result = $method->invoke($instance);
            }
            return $result;
        }

        /**
         * 显示页面错误
         * @param int $code
         * @param string $message
         * @return void
         */
        public static function showError($code, $message)
        {
            Response::sendHttpStatus($code);
            self::template(IN_PATH_FRAMEWORK . 'template/error.php', [
                'code' => $code,
                'message' => $message,
            ], true, true);
            die;
        }
    }

    IN_IS_CLI and inframe::initialize();
}